;;; init-editing-utils.el --- Day-to-day editing helpers -*- lexical-binding: t -*-
;;; Commentary:
;;; Code:

(defalias 'cz #'scratch-buffer)



(setq-default indent-tabs-mode nil)
(setq tab-always-indent 'complete)



(setq text-mode-ispell-word-completion nil)



;; Automatic parens pairing
(electric-pair-mode t)
;; https://www.reddit.com/r/emacs/comments/4xhxfw/how_to_tune_the_behavior_of_eletricpairmode/
(setq electric-pair-inhibit-predicate 'electric-pair-conservative-inhibit)
(setq show-paren-when-point-inside-paren t)
(setq show-paren-when-point-in-periphery t)



;; Delete selected words when typing
(delete-selection-mode 1)



;; Show trailing whitespace in some mode
(dolist (hook '(prog-mode-hook))
  (add-hook hook (lambda () (setq show-trailing-whitespace t))))



(with-eval-after-load 'symbol-overlay
  (setq symbol-overlay-idle-time 3)
  (define-key symbol-overlay-map (kbd "N") #'symbol-overlay-switch-forward)
  (define-key symbol-overlay-map (kbd "P") #'symbol-overlay-switch-backward)
  (define-key symbol-overlay-mode-map (kbd "M-i") #'symbol-overlay-put))
(add-hook 'prog-mode-hook 'symbol-overlay-mode)



;; Switch between the beginning/end of line or code
(global-set-key (kbd "C-a") #'mwim-beginning-of-code-or-line-or-comment)
(global-set-key (kbd "C-e") #'mwim-end-of-code-or-line)



;; Set key to quick mark content
(global-set-key (kbd "C-=") #'er/expand-region)



;;Jump to arbitrary positions in visible text and select text quickly
(global-set-key (kbd "C-;") #'avy-goto-char-timer)
(global-set-key (kbd "M-g l") #'avy-goto-line)
(with-eval-after-load 'avy
  (defun avy-action-kill-whole-line (pt)
    (save-excursion
      (goto-char pt)
      (kill-whole-line))
    (select-window
     (cdr
      (ring-ref avy-ring 0)))
    t)

  (defun avy-action-copy-whole-line (pt)
    (save-excursion
      (goto-char pt)
      (beginning-of-line)
      (skip-syntax-forward " " (line-end-position))
      (kill-ring-save (point) (line-end-position)))
    (select-window
     (cdr
      (ring-ref avy-ring 0)))
    t)

  (defun avy-action-yank-whole-line (pt)
    (avy-action-copy-whole-line pt)
    (save-excursion (yank))
    t)

  (defun avy-action-helpful (pt)
    (save-excursion
      (goto-char pt)
      (helpful-at-point))
    (select-window
     (cdr (ring-ref avy-ring 0)))
    t)

  (defun avy-action-embark (pt)
    (unwind-protect
        (save-excursion
          (goto-char pt)
          (embark-act))
      (select-window
       (cdr (ring-ref avy-ring 0))))
    t)

  (setf (alist-get ?w avy-dispatch-alist) 'avy-action-copy-whole-line
        (alist-get ?W avy-dispatch-alist) 'avy-action-yank-whole-line
        (alist-get ?k avy-dispatch-alist) 'avy-action-kill-whole-line
        (alist-get ?. avy-dispatch-alist) 'avy-action-embark
        (alist-get ?H avy-dispatch-alist) 'avy-action-helpful))



(defun delete-trailing-whitespace-except-current-line ()
  (interactive)
  (let ((begin (line-beginning-position))
        (end (line-end-position)))
    (save-excursion
      (when (< (point-min) begin)
        (save-restriction
          (narrow-to-region (point-min) (1- begin))
          (delete-trailing-whitespace)))
      (when (> (point-max) end)
        (save-restriction
          (narrow-to-region (1+ end) (point-max))
          (delete-trailing-whitespace))))))
(add-hook 'before-save-hook 'delete-trailing-whitespace-except-current-line)

(setq save-silently t)
(setq auto-save-visited-interval 1)
(auto-save-visited-mode)



(defun ctrlwei/copy-line (kill-content)
  "Copy current line without space in front of content.
With the universal argument, the text will also be killed."
  (interactive "P")
  (save-excursion
    (beginning-of-line)
    (skip-syntax-forward " " (line-end-position))
    (setq line-content (buffer-substring (point) (line-end-position)))
    (if (equal "" line-content)
        (message "Nothing here")
      (cond (kill-content
             (kill-region (point) (line-end-position))
             (message "Cut [ %s ]" line-content))
            (t
             (kill-ring-save (point) (line-end-position))
             (message "Copy [ %s ]" line-content))))))

(defun ctrlwei/pair-kill ()
  "Intelligent soft kill.
If cursor at end of line, join the current line to the following line,
else run `grammatical-edit-kill' when `grammatical-edit-mode' enabled,
when not in previous two situations, run `kill-line'."
  (interactive)
  (cond ((equal (point) (line-end-position))
         (join-line t))
        ((bound-and-true-p fingertip-mode)
         (fingertip-kill))
        (t
         (kill-line))))

(global-set-key (kbd "C-S-k") #'ctrlwei/copy-line)
(global-set-key (kbd "C-k") #'ctrlwei/pair-kill)



(define-minor-mode display-zero-width-chars-mode
  "Minor mode for displaying zero width characters in the current buffer."
  :lighter " DisplayZWC"
  (if display-zero-width-chars-mode
      (progn
        (setq-local glyphless-char-display
                    (copy-sequence glyphless-char-display))
        (display-zero-width-chars-mode--setup))
    (kill-local-variable 'glyphless-char-display)))

(defun display-zero-width-chars-mode--setup ()
    (let ((charnames (list "BYTE ORDER MARK"
                           "ZERO WIDTH NO-BREAK SPACE"
                           "ZERO WIDTH SPACE"
                           "NO-BREAK SPACE"
                           "RIGHT-TO-LEFT MARK"
                           "RIGHT-TO-LEFT OVERRIDE"
                           "LEFT-TO-RIGHT MARK"
                           "OBJECT REPLACEMENT CHARACTER"
                           "ZERO WIDTH JOINER"
                           "ZERO WIDTH NON-JOINER")))
      (dolist (name charnames)
        (set-char-table-range glyphless-char-display (char-from-name name) "ZWC"))))

(dolist (mode-hook '(prog-mode-hook
                     text-mode-hook
                     conf-mode-hook))
  (add-hook mode-hook 'display-zero-width-chars-mode))



(defun ctrlwei/copy-and-comment-region (beg end &optional region)
  "Copy region and comment region.
ugly copy of `kill-ring-save'"
  (interactive (list (mark) (point) 'region))
  (copy-region-as-kill beg end 'region)
  (comment-region beg end))

(global-set-key (kbd "M-W") #'ctrlwei/copy-and-comment-region)



(defun intellij-backspace (arg)
  "Make backspace act like Intellij did.
copied from https://emacs-china.org/t/backspace/18163/3"
  (interactive "*P")
  (if (or (region-active-p) (not (looking-back "^[[:space:]]*" (line-beginning-position))))
      (backward-delete-char-untabify (prefix-numeric-value arg))
    (let* ((tab-always-indent t)
           (beg (point))
           (end (progn (indent-for-tab-command) (point))))
      (when (<= beg end)
        (if (save-excursion (forward-line -1) (save-excursion (beginning-of-line) (looking-at "[[:space:]]*$")))
            (progn (delete-region (line-beginning-position 0) (line-beginning-position)) (back-to-indentation))
          (delete-indentation))))))

;; (define-key prog-mode-map (kbd "<backspace>") #'intellij-backspace)
(setq backward-delete-char-untabify-method 'hungry)



(defun multi-occur-in-this-mode ()
  "Show all lines matching REGEXP in buffers with this major mode."
  (interactive)
  (multi-occur
   (get-buffers-matching-mode major-mode)
   (car (occur-read-primary-args))))

(defun ctrlwei/multi-occur (major-mode)
  (interactive "P")
  (if major-mode
      (multi-occur-in-this-mode)
    (call-interactively #'multi-occur)))

(global-set-key (kbd "M-s O") 'ctrlwei/multi-occur)



(setq  whitespace4r-display-mappings
       '((space-mark            . [?·])
         (hard-space-mark       . [?¤])
         (zero-width-space-mark . [?┆])
         (tab-mark              . [?- ?>])))
(setq whitespace4r-style '(tabs hspaces zwspaces trailing))
(dolist (mode-hook '(prog-mode-hook
                     conf-mode-hook))
  (add-hook mode-hook 'whitespace4r-mode))



(undo-fu-session-global-mode)

(provide 'init-editing-utils)
;;; init-editing-utils.el ends here
